<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>PF: Issues with FTP</title>
<link rev="made" href="mailto:www@openbsd.org">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="resource-type" content="document">
<meta name="description"   content="the OpenBSD FAQ page">
<meta name="keywords"      content="openbsd,faq,pf">
<meta name="distribution"  content="global">
</head>

<!--
Copyright (c) 2003, Nick Holland <nick@openbsd.org>
Copyright (c) 2003, 2004, Joel Knight <enabled@myrealbox.com>

Permission to use, copy, modify, and distribute this documentation for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.

THE DOCUMENTATION IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS DOCUMENTATION INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS DOCUMENTATION
-->

<body bgcolor="#ffffff" text="#000000">
<!-- Passes validator.w3.org, please keep it this way;
please, use a max of 72 chars per line -->

<a href="../../index.html">
<img alt="[OpenBSD]" height=30 width=141 src="../../images/smalltitle.gif" border="0">
</a>
<p>
[<a href="perf.html">Previous: Performance</a>]
[<a href="index.html">Contents</a>]
[<a href="authpf.html">Next: Authpf: User Shell for Authenticating
Gateways</a>]

<p>
<h1><font color="#e00000">PF: Issues with FTP</font></h1>
<hr>

<h3>Table of Contents</h3>
<ul>
<li><a href="#modes">FTP Modes</a>
<li><a href="#client">FTP Client Behind the Firewall</a>
<li><a href="#server">PF "Self-Protecting" an FTP Server</a>
<li><a href="#natserver">FTP Server Protected by an External PF Firewall
Running NAT</a>
<li><a href="#info">More Information on FTP</a>
<li><a href="#tftp-proxy">Proxying TFTP</a>
</ul>

<hr>

<a name="modes"></a>
<h2>FTP Modes</h2>
FTP is a protocol that dates back to when the Internet was a small,
friendly collection of computers and everyone knew everyone else. At
that time the need for filtering or tight security wasn't necessary.
FTP wasn't designed for filtering, for passing through firewalls, or for
working with NAT.

<p>
You can use FTP in one of two ways: passive or active.  Generally, the
choice of active or passive is made to determine who has the problem
with firewalling.  Realistically, you will have to support both to have
happy users.

<p>
With active FTP, when a user connects to a remote FTP server and
requests information or a file, the FTP server makes a new
connection back to the client to transfer the requested data. This is
called the <i>data connection</i>.  To start, the FTP client chooses a
random port to receive the data connection on. The client sends the port
number it chose to the FTP server and then listens for an incoming
connection on that port.  The FTP server then initiates a connection to
the client's address at the chosen port and transfers the data.  This is a
problem for users attempting to gain access to FTP servers from behind a
NAT gateway.  Because of how NAT works, the FTP server initiates the data
connection by connecting to the external address of the NAT gateway on
the chosen port.  The NAT machine will receive this, but because it has
no mapping for the packet in its state table, it will drop the packet and
won't deliver it to the client.

<p>
With passive mode FTP (the default mode with OpenBSD's <a
href="http://www.openbsd.org/cgi-bin/man.cgi?query=ftp&amp;sektion=1"
>ftp(1)</a> client), the client requests that the server pick a random
port to listen on for the data connection. The server informs the client
of the port it has chosen, and the client connects to this port to
transfer the data. Unfortunately, this is not always possible or
desirable because of the possibility of a firewall in front of the FTP
server blocking the incoming data connection. OpenBSD's ftp(1) uses
passive mode by default; to force active mode FTP, use the -A flag to
ftp, or set passive mode to "off" by issuing the command "<tt>passive
off</tt>"  at the "<tt>ftp&gt;</tt>" prompt.


<a name="client"></a>
<h2>FTP Client Behind the Firewall</h2>
As indicated earlier, FTP does not go through NAT and firewalls very well.


<p>
Packet Filter provides a solution for this situation by redirecting FTP
traffic through an FTP proxy server. This process acts to "guide" your
FTP traffic through the NAT gateway/firewall, by actively adding needed
rules to PF system and removing them when done, by means of the PF
<a href="anchors.html">anchors</a> system. 
The FTP proxy used by PF is
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=ftp-proxy&amp;sektion=8&amp;manpath=OpenBSD+4.3"
>ftp-proxy(8)</a>.

<p>
To activate it, put something like this in the NAT section
of <tt>pf.conf</tt>:

<blockquote>
<tt>
nat-anchor "ftp-proxy/*"<br>
rdr-anchor "ftp-proxy/*"<br>
rdr on $int_if proto tcp from any to any port 21 -&gt; 127.0.0.1 \<br>
&nbsp;&nbsp;&nbsp;port 8021
</tt>
</blockquote>

<p>
The first two lines are a couple <a href="anchors.html">anchors</a>
which are used by ftp-proxy to add rules on-the-fly as needed to
manage your FTP traffic.
The last line redirects FTP from your clients to the ftp-proxy(8)
program, which is listening on your machine to port 8021.

<p>
You also need an anchor in the rules section:
<blockquote>
<tt>
anchor "ftp-proxy/*"
</tt>
</blockquote>

<p>
Hopefully it is apparent the proxy server has to be started and running on
the OpenBSD box. This is done by inserting the following line in
<tt>/etc/rc.conf.local</tt>:

<blockquote>
<tt>
ftpproxy_flags=""
</tt>
</blockquote>

<p>
The ftp-proxy program can be started as root to activate it without a
reboot.

<p>
ftp-proxy listens on port 8021, the same port the above <tt>rdr</tt>
statement is sending FTP traffic to.

<p>
To enable active mode connections, you need the '-r' switch on
ftp-proxy(8) (for this you had to run the old proxy with "-u root").


<a name="server"></a>
<h2>PF "Self-Protecting" an FTP Server</h2>
In this case, PF is running on the FTP server itself rather than a
dedicated firewall computer.  When servicing a passive FTP connection,
FTP will use a randomly chosen, high TCP port for incoming data.  By
default, OpenBSD's native FTP server 
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=ftpd&amp;sektion=8"
>ftpd(8)</a> uses the range 49152 to 65535.
Obviously, these must be passed through the filter rules, along with
port 21 (the FTP control port):

<blockquote>
<tt>
pass in on $ext_if proto tcp from any to any port 21 keep state<br>
pass in on $ext_if proto tcp from any to any port &gt; 49151 \<br>
&nbsp;&nbsp;&nbsp;keep state
</tt>
</blockquote>

<p>
Note that if you desire, you can tighten up that range of ports
considerably. In the case of the OpenBSD 
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=ftpd&amp;sektion=8"
>ftpd(8)</a> program, that is done using the 
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=sysctl&amp;sektion=8"
>sysctl(8)</a> variables <tt>net.inet.ip.porthifirst</tt> and
<tt>net.inet.ip.porthilast</tt>.

<a name="natserver"></a>
<h2>FTP Server Protected by an External PF Firewall Running NAT</h2>
In this case, the firewall must redirect traffic to the FTP server in
addition to not blocking the required ports.
In order to accomplish this, we turn again to ftp-proxy(8).

<p>
ftp-proxy(8) can be run in a mode that causes it to forward all FTP
connections to a specific FTP server.
Basically we'll setup the proxy to listen on port 21 of the firewall and
forward all connections to the back-end server.

<p>
Edit <tt>/etc/rc.conf.local</tt> and add the following:

<blockquote>
<tt>
ftpproxy_flags="-R 10.10.10.1 -p 21 -b 192.168.0.1"
</tt>
</blockquote>

<p>
Here 10.10.10.1 is the IP address of the actual FTP server, 21 is the
port we want ftp-proxy(8) to listen on, and 192.168.0.1 is the address
on the firewall that we want the proxy to bind to.

<p>
Now for the pf.conf rules:

<blockquote>
<tt>
ext_ip = "192.168.0.1"<br>
ftp_ip = "10.10.10.1"<br>
<br>
nat-anchor "ftp-proxy/*"<br>
nat on $ext_if inet from $int_if -&gt; ($ext_if)<br>
rdr-anchor "ftp-proxy/*"<br>
<br>
pass in on $ext_if inet proto tcp to $ext_ip port 21 \<br>
&nbsp;&nbsp;&nbsp;&nbsp;flags S/SA keep state<br>
pass out on $int_if inet proto tcp to $ftp_ip port 21 \<br>
&nbsp;&nbsp;&nbsp;&nbsp;user proxy flags S/SA keep state<br>
anchor "ftp-proxy/*"
</tt>
</blockquote>

<p>
Here we allow the connection inbound to port 21 on the external
interface as well as the corresponding outbound connection to the FTP
server.
The "user proxy" addition to the outbound rule ensures that only
connections initiated by ftp-proxy(8) are permitted.

<p>
Note that if you want to run ftp-proxy(8) to protect an FTP server as
well as allow clients to FTP out from behind the firewall that two
instances of ftp-proxy will be required.


<a name="info"></a>
<h2>More Information on FTP</h2>
More information on filtering FTP and how FTP works in general can be
found in this whitepaper:
<ul>
<li><a href="http://www.pintday.org/whitepapers/ftp-review.shtml">FTP
Reviewed</a>
</ul>


<a name="tftp-proxy"></a>
<h2>Proxying TFTP</h2>

<p>
Trivial File Transfer Protocol (TFTP) suffers from some of the same
limitations as FTP does when it comes to passing through a firewall.
Luckily, PF has a helper proxy for TFTP called
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=tftp-proxy&amp;sektion=8&amp;manpath=OpenBSD+4.3"
>tftp-proxy(8)</a>.

<p>
tftp-proxy(8) is setup in much the same way as ftp-proxy(8) was in
the <a href="#client">FTP Client Behind the Firewall</a> section above.

<blockquote>
<tt>
nat on $ext_if from $int_if -&gt; ($ext_if)<br>
rdr-anchor "tftp-proxy/*"<br>
rdr on $int_if proto udp from $int_if to port tftp -&gt; \<br>
&nbsp;&nbsp;&nbsp;&nbsp;127.0.0.1 port 6969<br>
<br>
anchor "tftp-proxy/*"
</tt>
</blockquote>

<p>
The rules above allow TFTP outbound from the internal network to TFTP
servers on the external network.

<p>
The last step is to enable tftp-proxy in
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=inetd.conf&amp;sektion=5"
>inetd.conf(5)</a> so that it listens on the same port that the
<tt>rdr</tt> rule specified above, in this case 6969.

<blockquote>
<tt>
127.0.0.1:6969  dgram   udp   wait  root  /usr/libexec/tftp-proxy tftp-proxy
</tt>
</blockquote>

<p>
Unlike ftp-proxy(8), tftp-proxy(8) is spawned from inetd.


<p>
[<a href="perf.html">Previous: Performance</a>]
[<a href="index.html">Contents</a>]
[<a href="authpf.html">Next: Authpf: User Shell for Authenticating
Gateways</a>]

<p>
<hr>
<a href="index.html"><img height="24" width="24" src="../../images/back.gif" border="0" alt="[back]"></a> 
<a href="mailto:www@openbsd.org">www@openbsd.org</a>
<br>
<small>$OpenBSD: ftp.html,v 1.26 2008/07/27 17:13:47 nick Exp $</small>

</body>
</html> 
